/*
 * This file is part of the quickTools support library, dubbed "LucasCode"
 *
 * Copyright (C) 2005-2008 Lucas Romero
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Diagnostics;
using System.Text;
using System.Threading;
using System.Net;
using System.IO;
using System.Drawing.Imaging;
using System.Text.RegularExpressions;
using ESLFileUploader.Helpers;
using ICSharpCode.SharpZipLib.Zip;
using System.Drawing;
using System.Collections;
using System.Globalization;
using System.Resources;
using HtmlAgilityPack;

namespace ESLFileUploader
{
   /* public class MyRequest : System.Net.HttpWebRequest
    {

        protected override System.Net.WebRequest GetWebRequest(Uri uri)
        {

            System.Net.HttpWebRequest webRequest = (System.Net.HttpWebRequest)base.GetWebRequest(uri);

            webRequest.KeepAlive = false;

            return webRequest;

        }

    }*/
	public enum ItemUploadStatus : int
	{
		Queued = 0,
		Converting = 1,
		Converted = 2,
		Uploading = 3,
		Uploaded = 4,
		/*Confirming,
		Confirmed,*/
		Aborted = 5,
	    Failed = 6,
		FinishedSuccessful = 7
	}
	public enum ItemError
	{
		None,
		ConvertingFailed,
		FTPUploadFailed,
		HTTPTimeout,
		HTTPNotFound
	}
	public enum UploadStatus
	{
		Stopped,
		Running,
		Failed
	}

	public class Uploader
	{

		#region private properties
		internal int m_RunningPreloads=0;
		internal int m_RunningThreads=0;
	
		internal System.Collections.ArrayList FTPConnections = new System.Collections.ArrayList();
		internal System.Collections.ArrayList UploadFiles = new System.Collections.ArrayList();
		internal UploadStatus m_UploadStatus = UploadStatus.Stopped;
		#endregion
		public ResourceManager lang;
		internal CultureInfo culture;
		public Cookie LoginCookie = null;
        public UInt32 WebUploadBufferSize=2048;
		public int MaxPreloads = 1;
		public int MaxConnections = 1;
        public int m_CurrentConnections = 0;
		public int MaxRetries = 1;
        public long ImageQuality = 100L;
		public string MatchURL;
		internal string SuccessfulLogPath="UploadedFiles.log";

        #region Status / Time Left / Speed
        /// <summary>
        /// Returns the current Speed in Byte/s (based on the time elapsed since last looking at this variable) max. refreshrate 1 Hz
        /// </summary>
        public double Speed
        {
            get
            {
                double speed = 0;
                foreach (UploadFile up in UploadFiles)
                {
                    if (up.Status == ItemUploadStatus.Uploading)
                        speed += up.Speed;
                }
                return speed;
            }
        }

        public TimeSpan TimeLeft
        {
            get
            {
                try
                {
                    long bytesleft = 0;
                    bool predictable = true;
                    foreach (UploadFile file in UploadFiles)
                    {
                        if (file.Status < ItemUploadStatus.Converted)
                        {
                            predictable = false;
                            break;
                        }
                        if (file.Status == ItemUploadStatus.Uploading)
                            bytesleft += file.m_FileSize - file.BytesProcessed;
                    }
                    if (!predictable)
                        return TimeSpan.Zero;
                    return new TimeSpan(0, 0, Convert.ToInt32(bytesleft / Speed));
                }
                catch
                {
                    return TimeSpan.Zero;
                }
            }
        }
        public long Progress
        {
            get
            {
                try
                {
                    long byteswritten = 0;
                    bool predictable = true;
                    foreach (UploadFile file in UploadFiles)
                    {
                        if (file.Status < ItemUploadStatus.Converted)
                        {
                            predictable = false;
                            break;
                        }
                        else if (file.Status >= ItemUploadStatus.Uploading)
                            byteswritten += file.BytesProcessed;
                    }
                    if (!predictable)
                        return 0;
                    return byteswritten;
                }
                catch
                {
                    return 0;
                }
            }
        }
        public long TotalBytes
        {
            get
            {
                try
                {
                    long bytestotal = 0;
                    bool predictable = true;
                    foreach (UploadFile file in UploadFiles)
                    {
                        if (file.Status < ItemUploadStatus.Converted)
                        {
                            predictable = false;
                            break;
                        }
                        else
                            bytestotal += file.m_FileSize;
                    }
                    if (!predictable)
                        return 0;
                    return bytestotal;
                }
                catch
                {
                    return 0;
                }
            }
        }
        #endregion

        
        #region Statistics
        public int UploadedDemoCount
        {
            get
            {
                return getSuccessfulItemCount(".dem") + getSuccessfulItemCount(".zip");
            }
        }
        public long UploadedDemoBytes
        {
            get
            {
                return getSuccessfulByteCount(".dem") + getSuccessfulByteCount(".zip");
            }
        }
        public int UploadedScreenshotCount
        {
            get
            {
                return getSuccessfulItemCount(".jpg") + getSuccessfulItemCount(".bmp");
            }
        }
        public long UploadedScreenshotBytes
        {
            get
            {
                return getSuccessfulByteCount(".jpg") + getSuccessfulByteCount(".bmp");
            }
        }
        public int UploadedAequitasCount
        {
            get
            {
                int i = 0;
                foreach (UploadFile file in UploadFiles)
                {
                    if (file.Status == ItemUploadStatus.FinishedSuccessful
                        && isAequitasFile(file.FullName))
                        i++;
                }
                return i;
            }
        }
        public long UploadedAequitasBytes
        {
            get
            {
                long i = 0;
                foreach (UploadFile file in UploadFiles)
                {
                    if (file.Status == ItemUploadStatus.FinishedSuccessful
                        && isAequitasFile(file.FullName))
                        i += file.FileSize;
                }
                return i;
            }
        }

        private bool isAequitasFile(string path)
        {
            if (Path.GetExtension(path).ToLower() == ".zip" && System.IO.Path.GetDirectoryName(path).ToLower().EndsWith(Path.DirectorySeparatorChar + "archive"))
                return true;
            else
                return false;
        }


        private int getSuccessfulItemCount(string ext)
        {
            int i = 0;
            foreach (UploadFile file in UploadFiles)
            {
                if (file.Status == ItemUploadStatus.FinishedSuccessful && Path.GetExtension(file.FileName).ToLower() == ext.ToLower() && !isAequitasFile(file.FullName))
                    i++;
            }
            return i;
        }

        private long getSuccessfulByteCount(string ext)
        {
            long i = 0;
            foreach (UploadFile file in UploadFiles)
            {
                if (file.Status == ItemUploadStatus.FinishedSuccessful && Path.GetExtension(file.FileName).ToLower() == ext.ToLower() && !isAequitasFile(file.FullName))
                    i += file.FileSize;
            }
            return i;
        }
        #endregion
        

		public Uploader(string cookie, string MatchURL)
		{
            LoginCookie = new Cookie("COOKIE", cookie, "/", ".esl.eu");
			lang = new System.Resources.ResourceManager("ESLFileUploader.lang",
				this.GetType().Assembly);
			if (CultureInfo.CurrentCulture.Name != "de-DE")
			{
				culture = CultureInfo.CreateSpecificCulture("");
			}
			else
			{
				culture = CultureInfo.CurrentCulture;
			}
			//m_Username = Username;
			//m_Password = Password;
			this.MatchURL = MatchURL;
		}
        public void Clear()
        {
            UploadFiles.Clear();
        }
		public UploadFile GetByID(int index)
		{
			return (UploadFile)UploadFiles[index];
		}
		/*public int AddFile(CompressedFile file)
		{
			return m_AddFile(file);
		}*/
		public int AddFile(UncompressedFile file)
		{
			return m_AddFile(file);
		}
		private int m_AddFile(UploadFile file)
		{
			file.m_Uploader = this;
			return UploadFiles.Add(file);
		}
		public UploadStatus Status
		{
			get
			{
				return m_UploadStatus;
			}
		}
		private void MainThread(object dummy)
		{
            Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;
			m_UploadStatus = UploadStatus.Running;
			Boolean finished=false;
			while (m_UploadStatus == UploadStatus.Running && !finished)
			{
				finished = true;
				foreach (UploadFile up in UploadFiles)
				{
					if (up.Status != ItemUploadStatus.FinishedSuccessful && (up.Status != ItemUploadStatus.Failed && up.Retries <= MaxRetries))
					{
						finished = false;
					}
				}
				
				if ((MaxPreloads > 0 && m_RunningPreloads < MaxPreloads))
				{
					foreach (UploadFile item in UploadFiles)
					{
						if (item.Status == ItemUploadStatus.Queued)
						{
							try
							{
								item.Preload();
							}
							catch (Exception ex)
							{
								item.m_UploadStatus = ItemUploadStatus.Failed;
								item.AddLog("Preloading failed: " + ex.Message);
							}
							break;
						}
					}
				}

                while (m_CurrentConnections < MaxConnections && AreFilesWaiting() && m_UploadStatus == UploadStatus.Running)
                {
                    foreach (UploadFile item in UploadFiles)
                    {
                        if (item.Status == ItemUploadStatus.Converted)
                        {
                            item.m_UploadStatus = ItemUploadStatus.Uploading;
                            m_CurrentConnections++;
                            item.Upload();
                            break;
                        }
                    }
                }
				Thread.Sleep(1000);
			}
			Stop();
		}

        private bool AreFilesWaiting()
        {
            bool result = false;
            foreach (UploadFile file in UploadFiles)
            {
                if (file.m_UploadStatus == ItemUploadStatus.Queued
                        || (file.m_UploadStatus == ItemUploadStatus.Failed && file.Retries < MaxRetries)
                        || file.m_UploadStatus == ItemUploadStatus.Uploading
                        || file.m_UploadStatus == ItemUploadStatus.Converting
                        || file.m_UploadStatus == ItemUploadStatus.Converted)
                {
                    result = true;
                    break;
                }
            }
            return result;
        }
        internal Cookie getLoginCookie(string Username, String Password)
        {
            StreamReader answer = null; ;
            Cookie cookie = null;
            string HTML = String.Empty;
            HttpWebRequest request;
            try
            {
                Debug.WriteLine("Getting Login Cookie");
                request = (HttpWebRequest)WebRequest.Create("http://www.esl.eu/login/save/?email=" + Username + "&password=" + Password);
                request.CookieContainer = new CookieContainer();
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                answer = new StreamReader(response.GetResponseStream());
                HTML = answer.ReadToEnd();
            }
            catch (Exception ex)
            {
                throw new Exception(String.Format(lang.GetString("LoginError", culture), ex.Message));
            }
            finally
            {
                if (answer != null)
                    answer.Close();
            }
            if (!Regex.Match(HTML, ">Logged in!<").Success)
            {
                //Login not successful
                throw new Exception(lang.GetString("PasswordWrong", culture));
            }
            else
            {
                cookie = request.CookieContainer.GetCookies(request.RequestUri)["COOKIE"];
            }

            return cookie;
        }
        private string GetHTML(string URL, Cookie cook)
        {
            string HTML = String.Empty;
            HttpWebRequest request;
            HttpWebResponse response;
            StreamReader answer = null;
            request = (HttpWebRequest)WebRequest.Create(URL);
            request.Timeout = 30000;
            request.CookieContainer = new CookieContainer();
            request.CookieContainer.Add(cook);
            //get response
            response = (HttpWebResponse)request.GetResponse();
            try
            {
                answer = new StreamReader(response.GetResponseStream());
                //while (m_UploadStatus != ItemUploadStatus.Aborted && answer.ReadLine() != String.Empty)
                //{
                //HTML += answer.ReadLine();
                //}
                HTML = answer.ReadToEnd();
            }
            catch (Exception ex)
            {
                throw new Exception(String.Format(lang.GetString("HTTPError", culture), ex.Message));
            }
            finally
            {
                answer.Close();
            }
            return HTML;
        }
		public void Start()
		{
			ThreadPool.QueueUserWorkItem(new WaitCallback(MainThread));
		}
	    public string getLog()
	    {
            StringBuilder sb = new StringBuilder();
            string TitleLine = "########################################################################";
            sb.Append(TitleLine + Environment.NewLine);
            string AboutLine = GetType().Assembly.GetName().Name + " " +
                               GetType().Assembly.GetName().Version.ToString();
            string AboutDeco = "";
            for (int i = 0; i < (TitleLine.Length) / 2 - 1 ; i++)
                AboutDeco += '#';
            sb.Append(AboutDeco + " " + AboutLine + " " + AboutDeco + Environment.NewLine);
            sb.Append(TitleLine + Environment.NewLine);
            foreach (UploadFile up in UploadFiles)
            {
                sb.Append(up.getLog());
            }
            return sb.ToString();
	    }
		public void Stop()
		{
            DateTime start = DateTime.Now;
			//m_UploadStatus = UploadStatus.Stopped;
			foreach (UploadFile file in UploadFiles)
			{
				if (file.m_UploadStatus != ItemUploadStatus.FinishedSuccessful/* && file.m_UploadStatus != ItemUploadStatus.Failed*/)
				{
					file.m_UploadStatus = ItemUploadStatus.Aborted;
				}
			}
            TimeSpan limit = new TimeSpan(0,0,5);
			while (m_RunningThreads > 0 && DateTime.Now - start < limit)
			{
				Thread.Sleep(100);
			}
            if (m_RunningThreads > 0)
            {
                foreach (UploadFile file in UploadFiles)
                {
                    if (file.UsedThread.ThreadState == System.Threading.ThreadState.Running)
                        file.UsedThread.Abort();
                }
            }
			StreamWriter w=null;
			try
			{
				w = new StreamWriter(File.Create("upload.log"));
                w.Write(getLog());
				w.WriteLine(lang.GetString("Thread Stopped",culture));
			}
			catch{}
			finally
			{
				if (w != null)
				{
					w.Close();
				}
			}
			m_UploadStatus = UploadStatus.Stopped;
		}
    }
    #region CompressedFile
    /*
    public class CompressedFile : UploadFile
	{
		public string ZipFile;
		public CompressedFile(string PathToZip,string PathInZip,string TargetName) : base(PathInZip,TargetName)
		{
			ZipFile = PathToZip;
		}
		override internal void CreateIndividualMemoryStream()
		{
			
			Stream input = null;
			string ext = string.Empty;
			string fileName = String.Empty;

			string zipfile = this.ZipFile;
			string CompressedFile = FullName;
			ZipInputStream s = new ZipInputStream(File.OpenRead(zipfile));
			ZipEntry theEntry;
			bool fileFound = false;
			while ((theEntry = s.GetNextEntry()) != null) 
			{
				if (theEntry.Name == m_FullName)
				{
					//m_StatusString = "Zipped file found";
					fileFound = true;
					break;
				}
			}

			if (fileFound)
			{
				string tempName = theEntry.Name;
				if (tempName.IndexOf("\\") != -1)
					tempName = tempName.Replace("/", "\\");
				ext = Path.GetExtension(tempName).ToLower();
				if (ext == ".jpg" || ext == ".zip")
				{
					// open input stream to read source file
					fileName = Path.GetFileName(tempName);
					input = s;
					m_FileSize = theEntry.Size;
				}
				else if (ext == ".bmp")
				{
					m_StatusString = m_Uploader.lang.GetString("Converting image",m_Uploader.culture);
					input = new MemoryStream();
					System.Drawing.Image Bild = Image.FromStream(s);
					Bild.Save(input,System.Drawing.Imaging.ImageFormat.Jpeg);
					Bild.Dispose();
					fileName = Path.GetFileNameWithoutExtension(tempName) + ".jpg";
					input.Seek(0,SeekOrigin.Begin);
					m_FileSize = input.Length;
				}
				else if (ext == ".dem")
				{
					m_StatusString = m_Uploader.lang.GetString("ReZip",m_Uploader.culture);
					fileName = Path.GetFileNameWithoutExtension(tempName) + ".zip";
					int zipbytes;
					const int BLOCK_SIZE = 2048;
					Byte[] zipbuffer = new Byte[BLOCK_SIZE];
					s.Close();
					ZipFile zp = new ZipFile(File.OpenRead(zipfile));
					Stream zfs = zp.GetInputStream(theEntry);

					input = new MemoryStream();
					ZipOutputStream z = new ZipOutputStream(input);
					z.SetLevel(9);
					ZipEntry entry = new ZipEntry(Path.GetFileName(tempName));
					//ZipEntry entry = new ZipEntry(Path.GetFileName(tempName));
					//entry.CompressionMethod = theEntry.CompressionMethod;
					//entry.Size = theEntry.Size;
					z.PutNextEntry(entry);
					while (((zipbytes = zfs.Read(zipbuffer,0,zipbuffer.Length)) > 0) && m_UploadStatus != ItemUploadStatus.Aborted)
					{
						//s.Read(zipbuffer,0,zipbuffer.Length);
						z.Write(zipbuffer,0,zipbytes);
                        m_BytesProcessed += zipbytes;
					}
					z.Finish();
					zfs.Close();
					//s.Close();
					
					m_FileSize = input.Length;
					input.Seek(0,SeekOrigin.Begin);
				}
			}
			m_BytesProcessed = 0;
			m_Data = input;
		}
    }
    */
    #endregion
    public class UncompressedFile : UploadFile
	{
		public UncompressedFile(string FileName,string TargetName) : base(FileName,TargetName)
		{
			//nothing special
		}
        private static ImageCodecInfo GetEncoderInfo(String mimeType)
        {
            int j;
            ImageCodecInfo[] encoders;
            encoders = ImageCodecInfo.GetImageEncoders();
            for (j = 0; j < encoders.Length; ++j)
            {
                if (encoders[j].MimeType == mimeType)
                    return encoders[j];
            }
            return null;
        }
		override internal void CreateIndividualMemoryStream()
		{
				Stream input = null;
				string ext = string.Empty;
				ext = Path.GetExtension(FullName).ToLower();
				if (ext == ".jpg" || ext == ".zip")
				{
					// open input stream to read source file
                    input = new FileStream(FullName, FileMode.Open, FileAccess.Read, FileShare.Read);
				}
				else if (ext == ".bmp")
				{
					m_StatusString = m_Uploader.lang.GetString("Converting image",m_Uploader.culture);
					AddLog(m_Uploader.lang.GetString("Converting image",m_Uploader.culture));
					input = new MemoryStream();
                    ImageCodecInfo myImageCodecInfo;
                    System.Drawing.Imaging.Encoder myEncoder;
                    EncoderParameter myEncoderParameter;
                    EncoderParameters myEncoderParameters;


                    // Get an ImageCodecInfo object that represents the JPEG codec.
                    myImageCodecInfo = GetEncoderInfo("image/jpeg");

                    // Create an Encoder object based on the GUID

                    // for the Quality parameter category.
                    myEncoder = System.Drawing.Imaging.Encoder.Quality;

                    // Create an EncoderParameters object.

                    // An EncoderParameters object has an array of EncoderParameter

                    // objects. In this case, there is only one

                    // EncoderParameter object in the array.
                    myEncoderParameters = new EncoderParameters(1);

                    // Save the bitmap as a JPEG file with quality level 25.
                    myEncoderParameter = new EncoderParameter(myEncoder, m_Uploader.ImageQuality);
                    myEncoderParameters.Param[0] = myEncoderParameter;


                    Image.FromFile(FullName).Save(input, myImageCodecInfo, myEncoderParameters);
					input.Seek(0,SeekOrigin.Begin);
				}
				else if (ext == ".dem")
				{
					m_StatusString = m_Uploader.lang.GetString("Zipping Demo",m_Uploader.culture);
					AddLog(m_Uploader.lang.GetString("Zipping Demo",m_Uploader.culture));
					int zipbytes;
					
					Byte[] zipbuffer = new Byte[ZIP_BLOCK_SIZE];
					input = new MemoryStream();
					ZipOutputStream z = new ZipOutputStream(input);
					z.SetLevel(9);
					ZipEntry entry = new ZipEntry(Path.GetFileName(FullName));
					z.PutNextEntry(entry);
					FileStream filein = new FileStream(FullName,FileMode.Open,FileAccess.Read,FileShare.Read);
					m_FileSize = filein.Length;
					while (((zipbytes = filein.Read(zipbuffer,0,zipbuffer.Length)) > 0) && m_UploadStatus != ItemUploadStatus.Aborted)
					{
						z.Write(zipbuffer,0,zipbytes);
						m_BytesProcessed += zipbytes;
					}
					filein.Close();
					z.Finish();
					input.Seek(0,SeekOrigin.Begin);
				}
				m_FileSize = input.Length;
				m_Data = input;
			}
			
	}
		
	abstract public class UploadFile
	{
			#region private properties
            internal const int ZIP_BLOCK_SIZE = 2048;
			internal ItemUploadStatus m_UploadStatus = ItemUploadStatus.Queued;
			internal string m_TargetName;
			internal string m_FullName;
			internal Uploader m_Uploader;
			internal Stream m_Data;
			internal int m_Retries = 0;
			internal ItemError m_Error=ItemError.None;
			#endregion

			#region public properties
			public int Retries
			{
				get
				{
					return m_Retries;
				}
			}	

			public ItemError Error
			{
				get
				{
					return m_Error;
				}
			}
			public string FullName
			{
				get
				{
					return m_FullName;
				}
			}

			public string TargetName
			{
				get
				{
					return m_TargetName;
				}
			}

			public string FileName
			{
				get
				{
					return Path.GetFileName(m_FullName);
				}
			}

			public int Progress
			{
				get
				{
					try
					{
						float db = (m_BytesProcessed * 100 / m_FileSize);
						if (db > 100) 
						{
							db = 100;
						}
						return (int)db;
					}
					catch
					{
						return 0;
					}
				}
			}

			public int BandwidthLimit=0;
			#endregion

			#region procedures

			internal void Preload()
			{
				m_Uploader.m_RunningPreloads++;
				ThreadPool.QueueUserWorkItem(new WaitCallback(CreateMemoryStream));
			}
			internal void CreateMemoryStream(object dummy)
			{
				m_Uploader.m_RunningThreads++;
				m_UploadStatus = ItemUploadStatus.Converting;
				m_StatusString = m_Uploader.lang.GetString("Converting file",m_Uploader.culture);
				m_BytesProcessed = 0;
				try
				{
					CreateIndividualMemoryStream();
				}				
				catch(Exception ex)
				{
					m_UploadStatus = ItemUploadStatus.Failed;
					m_Error = ItemError.ConvertingFailed;
					m_StatusString = m_Uploader.lang.GetString("Error",m_Uploader.culture) + ex.Message;
				}
				if (m_UploadStatus != ItemUploadStatus.Failed && m_UploadStatus != ItemUploadStatus.Aborted)
				{
					m_UploadStatus = ItemUploadStatus.Converted;
					m_StatusString = m_Uploader.lang.GetString("Conversion complete",m_Uploader.culture);
				}
				m_Uploader.m_RunningPreloads--;
                m_Uploader.m_RunningThreads--;
			}
			virtual internal void CreateIndividualMemoryStream()
			{
				//virtual
			}

		
			#endregion

			public string getLog()
			{
				System.Text.StringBuilder sb = new System.Text.StringBuilder();
				sb.Append("########## quickLoad log - file: " + FileName.ToUpper() + " #########" + Environment.NewLine);
				foreach (LogEntry log in Log)
				{
					sb.Append("[" + log.TimeStamp.ToLongTimeString() + "] " + log.Message + Environment.NewLine);
				}
				sb.Append(Environment.NewLine);
				return sb.ToString();
			}
		    /*
			public Cookie getLoginCookie(string Username, String Password)
			{
			Cookie cookie=null;
            HttpWebRequest request=null;
            StreamReader answer = null;
			string HTML = String.Empty;
			AddLog(String.Format(m_Uploader.lang.GetString("ESL Login",m_Uploader.culture),Username));
			m_StatusString = "Logging in to ESL Europe...";


			try
			{
                request = (HttpWebRequest)WebRequest.Create("http://www.esl.eu/login/save/?email=" + Username + "&password=" + Password);
                request.CookieContainer = new CookieContainer();
                HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                answer = new StreamReader(response.GetResponseStream());
				HTML = answer.ReadToEnd();
			}
			catch (Exception ex)
			{
				//AddLog(String.Format(m_Uploader.lang.GetString("LoginError",m_Uploader.culture),ex.Message));
                throw new Exception(String.Format(m_Uploader.lang.GetString("LoginError", m_Uploader.culture), ex.Message));
			}
			finally
			{
                if (answer != null)
				    answer.Close();
			}
			if (!Regex.Match(HTML,">Logged in!<").Success)
			{
				//Login not successful
				AddLog(m_Uploader.lang.GetString("PasswordWrong",m_Uploader.culture));
			}
			else
			{
				AddLog(m_Uploader.lang.GetString("LoginSuccessful",m_Uploader.culture));
				cookie = request.CookieContainer.GetCookies(request.RequestUri)["COOKIE"];
			}

			return cookie;
		}
        */
			/// <summary>
			/// Initializes the Uploader. No connection to a server is made at this point.
			/// </summary>
			/// <param name="Username">The ESL-Loginname (E-Mail address)</param>
			/// <param name="Password">The ESL-Password</param>
			/// <param name="LogCommand">The Command that should be used for custom logging (can be null if not needed)</param>
			/// <param name="FinishedCommand">The Command that is executed when a file was uploaded (also if failed)</param>
			public UploadFile(string fileName, string targetName)
			{
				//m_Uploader = UploadContainer;
				m_FullName = fileName;
				m_TargetName = targetName;
            }

            #region Properties
            /// <summary>
			/// Returns the size of the file that is currently being uploaded
			/// </summary>
			public long FileSize
			{
				get
				{
					return m_FileSize;
				}
			}
			/// <summary>
			/// Returns the amount of bytes uploaded of the file that is currently being uploaded
			/// </summary>
			public long BytesProcessed
			{
				get
				{
					return m_BytesProcessed;
				}
			}
			/// <summary>
			/// bytes transferred of the file that is currently being uploaded
			/// </summary>
			internal long m_BytesProcessed = 0;
			/// <summary>
			///size of the file that is currently being uploaded
			/// </summary>
			internal long m_FileSize = 0;
			/// <summary>
			/// Time of last speed-measurement (needed to calc the kb/s)
			/// </summary>
			private DateTime LastSpeedMeasurementTime = DateTime.Now;
			/// <summary>
			/// Number of Bytes that have been transferred until the last measurement
			/// </summary>
			private long LastSpeedMeasurementByteCount = 0;
			private double m_Speed;
			/// <summary>
			/// Returns the current Speed in Byte/s (based on the time elapsed since last looking at this variable) max. refreshrate 1 Hz
			/// </summary>
			public double Speed
			{
				get
				{
					try
					{
						if (m_UploadStatus == ItemUploadStatus.Uploading || m_UploadStatus == ItemUploadStatus.Converting)
						{
							if (((TimeSpan)(DateTime.Now - LastSpeedMeasurementTime)) > TimeSpan.FromSeconds(1))
							{
								m_Speed = Math.Round((m_BytesProcessed - LastSpeedMeasurementByteCount) / (((TimeSpan)(DateTime.Now - LastSpeedMeasurementTime)).TotalMilliseconds) * 1000);
                                if (m_Speed < 0)
                                    m_Speed = 0;
								LastSpeedMeasurementByteCount = m_BytesProcessed;
								LastSpeedMeasurementTime = DateTime.Now;
							}
						}
						else
						{
							m_Speed = 0;
						}
						
					}
					catch
					{
						m_Speed = 0;
					}
					return m_Speed;
				}
			}
			/// <summary>
			/// Holds the current status
			/// </summary>
			internal string m_StatusString = string.Empty;
			public string StatusString
			{
				get
				{
					return m_StatusString;
				}
			}
			public ItemUploadStatus Status
			{
				get
				{
					return m_UploadStatus;
				}
            }
            #endregion
            internal Thread UsedThread = null;
			/// <summary>
			/// Start uploading a file (threaded)
			/// </summary>
			/// <param name="MatchURL">The URL to the link the file should be added to</param>
			/// <param name="Resume">Use FTP-Resume</param>
			internal void Upload()
			{
				ThreadPool.QueueUserWorkItem(new WaitCallback(StartUpload));
			}

			private void StartUpload(object dummy)
			{
                UsedThread = Thread.CurrentThread;
				m_UploadStatus = ItemUploadStatus.Uploading;
                m_Uploader.m_RunningThreads++;
				m_BytesProcessed = 0;
                m_FileSize = m_Data.Length;
                m_Data.Seek(0,SeekOrigin.Begin);
			    
				AddLog(String.Format(m_Uploader.lang.GetString("Upload started",m_Uploader.culture),FileName,m_Uploader.MatchURL,this.GetType().ToString(),Convert.ToString(m_Retries+1),Convert.ToString(m_Uploader.MaxRetries+1)));
                m_StatusString = m_Uploader.lang.GetString("FTP Uploading", m_Uploader.culture);
				string mFile = string.Empty;
				string targetname = string.Empty;
				LastSpeedMeasurementTime = DateTime.Now;
                
				//UploadFile curitem = (UploadFile)thisStream data ;
				//mFile = FullName;
				//targetname = TargetName;
                Stream data=null;
                StreamReader html = null;
				//string m_UppedName = string.Empty;
                try
                {
                    if (string.IsNullOrEmpty(m_Uploader.MatchURL))
                        throw new Exception("Missing matchlink!");
                    //ASCIIEncoding encoding = new ASCIIEncoding();
                    UTF8Encoding encoding = new UTF8Encoding();
                    int bytes;
                    String boundary = "AaB03x";
                    //String boundary2 = "-----------------------------323682726622763";
                    //http://janm.web.my-ct.de/formtest.php?match=asd
                    string content = "";
                    string matchid = Regex.Match(m_Uploader.MatchURL, "match/(\\d*?)/").Groups[1].ToString();
                    content += String.Format("--AaB03x\r\nContent-Disposition: form-data; name=\"match\"\r\n\r\n{0}\r\n", matchid);
                    content += "--AaB03x\r\nContent-Disposition: form-data; name=\"uploadtype\"\r\n\r\nhttp\r\n";
                    content += "--AaB03x\r\nContent-Disposition: form-data; name=\"MAX_FILE_SIZE\"\r\n\r\n104857600\r\n";
                    content += "--AaB03x\r\nContent-Disposition: form-data; name=\"id\"\r\n\r\n\r\n";
                    content += "--AaB03x\r\nContent-Disposition: form-data; name=\"defaulttitle[]\"\r\n\r\nRound 1\r\n";
                    // application/zip oder image/jpeg
                    string type = "", name = "", ext = "";
                    ext = Path.GetExtension(m_FullName).ToLower();
                    if (ext == ".jpg" || ext == ".bmp")
                    {
                        type = "image/jpeg";
                        name = Path.GetFileNameWithoutExtension(m_FullName) + ".jpg";
                    }
                    else if (ext == ".zip" || ext == ".dem")
                    {
                        type = "application/zip";
                        name = Path.GetFileNameWithoutExtension(m_FullName) + ".zip";
                    }
                    content += String.Format("--AaB03x\r\nContent-Disposition: form-data; name=\"title[]\"\r\n\r\n{0}\r\n--AaB03x\r\nContent-Disposition: file; name=\"datafile[]\"; filename=\"{1}\"\r\nContent-Type: {2}\r\n\r\n", m_TargetName, name, type);
                    String postsending = "\r\n--AaB03x\r\n";
                    byte[] buffer = new byte[m_Uploader.WebUploadBufferSize];
                    HttpWebRequest r = (HttpWebRequest)WebRequest.Create(m_Uploader.MatchURL.Replace("match/" + matchid, "matchmedia/savedata"));
                    r.CachePolicy = new System.Net.Cache.RequestCachePolicy(System.Net.Cache.RequestCacheLevel.NoCacheNoStore);
                    //r.AllowWriteStreamBuffering = true;
                    //r.ReadWriteTimeout = 10000;
                    //r.Timeout = 10000;
                    r.Timeout = Timeout.Infinite;
                    r.KeepAlive = false;
                    r.ProtocolVersion = HttpVersion.Version10;
                    //r.TransferEncoding = 
                    //r.Referer = "http://www.esl.eu/de/cs/2on2/ladder/matchmedia/adddata/6514649/";
                    r.CookieContainer = new CookieContainer();
                    r.CookieContainer.Add(m_Uploader.LoginCookie);
                    
                    r.Method = "POST";
                    r.ContentType = "multipart/form-data; boundary=AaB03x";
                    r.ContentLength = m_Data.Length + encoding.GetByteCount(content) + encoding.GetByteCount(postsending);
                    data = r.GetRequestStream();
                    //StreamWriter w = new StreamWriter(data);

                    data.Write(encoding.GetBytes(content), 0, encoding.GetByteCount(content));
                    while ((bytes = m_Data.Read(buffer, 0, buffer.Length)) > 0 && m_UploadStatus != ItemUploadStatus.Aborted)
                    {
                        data.Write(buffer, 0, bytes);
                        m_BytesProcessed += buffer.Length;
                        //w.Write(buffer, 0, bytes);
                    }
                    if (m_UploadStatus == ItemUploadStatus.Aborted)
                        throw new Exception(m_Uploader.lang.GetString("FTP Upload Aborted", m_Uploader.culture));
                    data.Write(encoding.GetBytes(postsending), 0, encoding.GetByteCount(postsending));
                    data.Close();
                    HttpWebResponse response = (HttpWebResponse)r.GetResponse();
                    html = new StreamReader(response.GetResponseStream());
                    string HTML = html.ReadToEnd();
                    /*StreamWriter w=null;
                    try
                    {
                        w = new StreamWriter("html.txt");
                        w.Write(HTML);
                    }
                    finally
                    {
                        if (w != null)
                            w.Close();
                    }*/

                    if(!string.IsNullOrEmpty(HTML))
                    {
                        if (Regex.Match(HTML, @"<a href="".*?/login/""><img.*?src=""http://www.eslgfx.net/gfx/logos/playerphotos/default_small.gif"".*?></a>", RegexOptions.Singleline).Success)
                            throw new Exception(m_Uploader.lang.GetString("NotLoggedIn", m_Uploader.culture));
                        HtmlAgilityPack.HtmlDocument d = new HtmlAgilityPack.HtmlDocument();
                        d.LoadHtml(HTML);
                        if (d.DocumentNode.SelectSingleNode(@"//div[@id=""main_content""]") == null)
                            throw new Exception(m_Uploader.lang.GetString("ESLOffline", m_Uploader.culture));
                        //if (!Regex.Match(HTML, @"<a.*?href=""/change_timezone/.*?"">(\d\d/\d\d/\d\d \d\d:\d\d)h.*?</a>", RegexOptions.Singleline).Success)
                            //throw new Exception(m_Uploader.lang.GetString("ESLOffline", m_Uploader.culture));
                    }
                    else
                        throw new Exception(m_Uploader.lang.GetString("ErrorReadingResponse", m_Uploader.culture));

                    
                    //browser.DocumentText = source.ReadToEnd();
                    response.Close();


                    //AddLog("UPLOAD COMPLETE");
                }
                catch (Exception ex)
                {
                    if (m_UploadStatus != ItemUploadStatus.Aborted)
                    {
                        m_UploadStatus = ItemUploadStatus.Failed;
                        m_Error = ItemError.FTPUploadFailed;
                    }
                    m_StatusString = ex.Message;
                    AddLog(ex.Message);
                    AddLog("Inner Exception: " + ex.InnerException);
                    AddLog(ex.StackTrace);
                }
                finally
                {
                    if (html != null)
                        html.Close();
                }
                m_Uploader.m_CurrentConnections--;

				if (m_UploadStatus != ItemUploadStatus.Failed && m_UploadStatus != ItemUploadStatus.Aborted)
				{
                    m_UploadStatus = ItemUploadStatus.FinishedSuccessful;
                    AddLog(m_Uploader.lang.GetString("Upload finished", m_Uploader.culture));
                    m_StatusString = m_Uploader.lang.GetString("Upload finished", m_Uploader.culture);
                    m_Error = ItemError.None;
				}
				if (m_UploadStatus == ItemUploadStatus.Failed && m_Retries < m_Uploader.MaxRetries)
				{
					m_Retries++;
					m_UploadStatus = ItemUploadStatus.Converted;
				}
				else
					m_Data.Close();
                m_Uploader.m_RunningThreads--;
			}
			private System.Collections.ArrayList Log = new System.Collections.ArrayList();
			/// <summary>
			/// Add Log Entry
			/// </summary>
			internal void AddLog(string Entry)
			{
				Entry = Entry.Replace(Environment.NewLine,"");
				LogEntry logentry;
				logentry.TimeStamp = DateTime.Now;
				logentry.Message = Entry;
				Log.Add(logentry);
			}
			public void Cancel()
			{
				m_UploadStatus = ItemUploadStatus.Aborted;
			}
			private string GetHTML(string URL,Cookie cook)
			{
				string HTML = String.Empty;
				HttpWebRequest request;
				HttpWebResponse response;
				StreamReader answer = null;
				request = (HttpWebRequest)WebRequest.Create(URL);
				request.Timeout = 30000;
				request.CookieContainer = new CookieContainer();
				request.CookieContainer.Add(cook);
				//get response
				response = (HttpWebResponse) request.GetResponse();
				try
				{
					answer = new StreamReader(response.GetResponseStream());
					//while (m_UploadStatus != ItemUploadStatus.Aborted && answer.ReadLine() != String.Empty)
					//{
						//HTML += answer.ReadLine();
					//}
					HTML = answer.ReadToEnd();
				}
				catch (Exception ex)
				{
					throw new Exception(String.Format(m_Uploader.lang.GetString("HTTPError",m_Uploader.culture),ex.Message));
				}
				finally
				{
					answer.Close();
				}
				return HTML;
			}

			/// <summary>
			/// Calculates the time remaining for current file based on the speed given in kb/s
			/// </summary>
			/// <param name="DLSpeed">The upload-speed in kb/s</param>
			/// <returns>TimeSpan the upload needs to finish</returns>
			public TimeSpan TimeRemaining(double DLSpeed)
			{
				if (DLSpeed <= 0)
					return TimeSpan.Zero;
				Double dbl=0;
				try
				{
					dbl = m_FileSize - m_BytesProcessed;
				}
				catch
				{
					dbl=0;
				}
				if (dbl < 0)
					dbl = 0;
				//float flt = ;
				if (dbl / DLSpeed < 0)
					return TimeSpan.Zero;
				else
					return new TimeSpan(0,0,0,(int)(dbl / DLSpeed),0);
			}
		}

	public struct LogEntry
	{
		public DateTime TimeStamp;
		public String Message;
	}
}
